package dse.modules;

import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import java.util.Vector;

import testing.TestHelper;
import dse.Cluster;
import dse.DataObject;
import dse.DataObjectAccessRanking;
import dse.DseException;
import dse.Network;
import dse.ExperimentConfiguration;
import dse.modules.eln.EarthLikeNetwork;

public class GeneralDataObjectAccessRanker implements DataObjectAccessRanker {
	private Random random;
	private double diff;
	
	
	public void initalize (
			ExperimentConfiguration systemSettings,
			Map<String, String> moduleSettings
			)
	throws DseException
	{
		random = systemSettings.getRandom();
		
		ModuleUtil.SettingsReader sr = ModuleUtil.getSettingsReader(
				this.getClass(), moduleSettings
				);
		diff = sr.getDouble("diff") / 100.0;
	}

	
	public Map<Cluster, DataObjectAccessRanking> generateAccessRankings (
			Network network, 
			Vector<DataObject> dataObjects
			)
	{
		// Generate system wide rankings first!
		int numobjs = dataObjects.size();
		DataObjectAccessRanking globalRankings = 
			new DataObjectAccessRanking (numobjs);
		
		for (int a = 0; a < numobjs; a++) {
			DataObject dobj = dataObjects.get(a);
			// Pick a spot in the array to put the object
			int desiredRank = random.nextInt(numobjs) + 1;
			while (true) {
				if (globalRankings.get(desiredRank) == null) {
					globalRankings.set(desiredRank, dobj);
					break;
				} 
				
				// Otherwise loop back and try to put it in a different spot
				desiredRank++;
				if (desiredRank > numobjs)
					desiredRank = 1;
			}
		}
		
		// Make sure nothing is null...
		for (DataObject dobj : globalRankings)
			assert (dobj != null);
		// Make sure everything occurs only once!
		for (DataObject dobj : dataObjects) {
			int count = 0;
			for (DataObject dobj2 : globalRankings)
				if (dobj.id() == dobj2.id())
					count++;
			assert (count == 1);
		}
			
		
		// Now change the global ranking a bit for each cluster
		Hashtable<Cluster, DataObjectAccessRanking> results = 
			new Hashtable<Cluster, DataObjectAccessRanking>();
		
		for (Cluster c : network.clusters()) {
			DataObjectAccessRanking copyR = globalRankings.clone();
			
			// Now mix up the copy some :)
			if (dataObjects.size() < 2){
				results.put(c, copyR);
				continue;
			}
			
			int numChangesToMake = (int)(diff*dataObjects.size());
			
			while (numChangesToMake > 0) {
				numChangesToMake--;
				
				// Pick two indicies to swap :)
				int first = random.nextInt(dataObjects.size());
				int second = 0;
				do {
					second = random.nextInt(dataObjects.size());
				} while (first == second);
				
				DataObject do1 = copyR.get(first+1);
				DataObject do2 = copyR.get(second+1);
				copyR.set(first+1, do2);
				copyR.set(second+1, do1);
			}
			
			results.put(c,copyR);
		}
		
		return results;
	}
	
	
	
	
	
	


	/**
	 * For testing :)
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main (String [] args) 
	throws Exception
	{
		if (args.length != 4 && args.length != 5) {
			System.out.println(
					"Usage: java -cp ... " +
					"dse.modules.GeneralDataObjectAccessRanker " +
					"<nodes> <max-cluster-size> <num-data-objs> <diff> (seed)"
					);
			System.exit(-1);
		}
		
		
		ExperimentConfiguration ss = TestHelper.createSystemSettings(args, 4);
		
		EarthLikeNetwork network = TestHelper.createELNetwork(
				ss, args, 0, -1, -1
				);
		
		TestHelper.createClusters(ss, args, 1, network);
		
		Vector<DataObject> dataObjects = TestHelper.createDataObjects(
				ss, args, 2, -1, -1, -1, -1
				);
		
		
		GeneralDataObjectAccessRanker gdoar = 
			new GeneralDataObjectAccessRanker ();
		
		Hashtable<String, String> props = new Hashtable<String, String>();
		props.put("diff", args[3]);
		
		gdoar.initalize(ss, props);
		
		Map<Cluster,DataObjectAccessRanking> rankings = 
			gdoar.generateAccessRankings(network, dataObjects);
		
		for (Cluster c : rankings.keySet()) {
			System.out.print("Cluster " + c.id() + ":\t");
			for (DataObject dobj : rankings.get(c)) {
				System.out.print(dobj.id()+" ");
			}	
			System.out.println();
		}
		
		System.out.println("Done.");
	}
}
